import unittest
import merchant
import world
import movement
import town
import engine


class MerchantTest(unittest.TestCase):
    """Test case for the 'merchant' module"""
    def setUp(self):
        

        
        self.world1 = world.World()
        self.world1.populate()
        self.world2 = world.World()
        self.world2.populate()
        self.world3 = world.World()
        self.world3.populate(7, 10)
        self.state1 = merchant._MerchantState(self.world1, 1, 1, [])
        self.state2 = merchant._MerchantState(self.world1, 1, 1, [town.FOOD])
        
    
    def test_merchant_state(self):
        state1 = merchant._MerchantState(self.world1, 1, 1, [])
        state1a = merchant._MerchantState(self.world1, 1, 1, [])
        
        self.assertEquals(state1, state1a)
        
        state2 = merchant._MerchantState(self.world2, 1, 1, [])
        state3 = merchant._MerchantState(self.world1, 3, 1, [])
        state4 = merchant._MerchantState(self.world1, 1, 3, [])
        state5 = merchant._MerchantState(self.world1, 1, 1, ["goo"])
        
        states = [state1, state2, state3, state4, state5]
        
        for i in range(len(states)):
            state = states[i]
            for j in range(i + 1, len(states)):
                other = states[j]
                self.assertEqual(state == other, i == j)
                
        self.assertEqual(state3.world, self.world1)
        self.assertEqual(state3.x, 3)
        self.assertEqual(state3.y, 1)
        self.assertEqual(state3.goods, [])
        self.assertEqual(state5.goods, ["goo"])
    
    def test_set_location(self):
        
        w = world.World()
        w.populate()
        m = merchant.Merchant(w)
        m.set_location((0, 0))
        t = w.get_town(0, 0)
        self.assertTrue(m in t.merchants)
        
        m.set_location((0, 0))
        self.assertTrue(m in t.merchants)

        m.set_location((0, 1))
        self.assertFalse(m in t.merchants)

        t = w.get_town(0, 1)
        self.assertTrue(m in t.merchants)

    
    def test_action(self):
        action  = merchant._Action()
        
        self.assertRaises(Exception, action._get_reward, (action))
        self.assertRaises(Exception, action.is_valid, (action))
        
        action.is_valid = lambda: True
        
        self.assertRaises(Exception, action.get_reward, (action))
        
        action._get_reward = lambda: 10
        
        self.assertEqual(action.get_reward(), 10)
        
        action.is_valid = lambda: False
        
        self.assertRaises(Exception, action.get_reward, (action))
        
        
        
    def test_move_action(self):
        state = merchant._MerchantState(self.world1, 1, 1, [])
        
        #test move action
        try:
            action = merchant._MoveAction(state, (2, 4))
            self.assertTrue(False)
        except Exception:
            "do nothing"
        
        try:
            action = merchant._MoveAction(state, (1, 1))
            self.assertTrue(False)
        except Exception:
            "do nothing"
            
        for d in movement.ALL_DIRECTIONS:
            action = merchant._MoveAction(state, d)
            expected = movement.get_route(state.world, d, (1, 1))
            self.assertEqual(action.route, expected)
            self.assertEqual(action.get_reward(), -expected.get_cost())
        
        
        #Test get move action
        m = merchant.PuppetMerchant(self.world1)
        
        for d in movement.ALL_DIRECTIONS:
            self.assertRaises(merchant.ActionException,
                              merchant.get_move_action,
                              m, d)
        
        m.set_location((1, 1))
        
        for d in movement.ALL_DIRECTIONS:
            action = merchant.get_move_action(m, d)
            self.assertTrue(action)
            m.set_action(action)
            m.do_turn(engine.EVENTS)
            assert(m.location == d((1, 1)))
            m.set_location((1, 1))
        
        #Test equals and hashable
        state1 = merchant._MerchantState(self.world1, 2, 3, [town.FOOD])
        state2 = merchant._MerchantState(self.world1, 2, 3, [town.FOOD])
        state3 = merchant._MerchantState(self.world1, 5, 3, [])
        
        
        for d in movement.ALL_DIRECTIONS:
            action1 = merchant._MoveAction(state1, d)
            action2 = merchant._MoveAction(state2, d)
            action3 = merchant._MoveAction(state3, d)
            #Test equals
            self.assertTrue(action1 == action1)
            self.assertTrue(action1 == action2)
            self.assertFalse(action1 == action3)
            self.assertFalse(action2 == action3)
            #Test hashable
            hash = {}
            hash[action1] = 1
            hash[action2] = 2
            self.assertEqual(hash[action1], 2)
        
            
    def test_buy_sell_actions(self):
        state = self.state1
        
        buy = merchant._BuyAction(state, town.FOOD)
        state.goods.append(town.FOOD)
        sell = merchant._SellAction(state, town.FOOD)
        expected = self.world1.get_town(state.x, state.y).prices[town.FOOD]
        
        
        self.assertTrue(buy)
        self.assertTrue(sell)
        self.assertEqual(-expected, buy.get_reward())
        self.assertEqual(expected, sell.get_reward())
        
        m1 = merchant.PuppetMerchant(self.world1)
        m2 = merchant.PuppetMerchant(self.world1)
        m2.goods.append(town.FOOD)
        
        for g in town.ALL_GOODS:
            self.assertRaises(merchant.ActionException,
                              merchant.get_buy_action,
                              m1, g)
            self.assertRaises(merchant.ActionException,
                              merchant.get_sell_action,
                              m1, g)
            
        m1.set_location((1, 1))
        m2.set_location((1, 1))
        
        for g in town.ALL_GOODS:
            #====BUY====
            action = merchant.get_buy_action(m1, g)
            self.assertTrue(action)
            m1.set_action(action)
            m1.do_turn(engine.EVENTS)
            assert(m1.goods == [g])
            m1.goods = []
            #====SELL====
            action = merchant.get_sell_action(m2, g)
            if action:
                self.assertEqual(m2.goods[0], g)
                m2.set_action(action)
                m2.do_turn(engine.EVENTS)
                assert(m2.goods == [])
                m2.goods.append(g)
            else: self.assertNotEqual(m2.goods[0], g)
            
            
            
        
        


if __name__ == '__main__':
    unittest.main()
    
def get_suite():
    return unittest.makeSuite(MerchantTest)
